package com.study.principle;

import java.util.HashMap;
import java.util.Map;

/**
 * 6大设计原则：里氏替换原则
 *
 * 里氏替换原则包含以下4层含义：
 *      子类可以实现父类的抽象方法，但是不能覆盖父类的非抽象方法。
 *      子类中可以增加自己特有的方法。
 *      当子类覆盖或实现父类的方法时，方法的前置条件（即方法的形参）要比父类方法的输入参数更宽松。
 *      当子类的方法实现父类的抽象方法时，方法的后置条件（即方法的返回值）要比父类更严格。
 *
 * 里氏替换原则的关键点在于不能覆盖父类的非抽象方法。父类中凡是已经实现好的方法，实际上是在设定一系列的规范和契约，
 * 虽然它不强制要求所有的子类必须遵从这些规范，但是如果子类对这些非抽象方法任意修改，就会对整个继承体系造成破坏。而里氏替换原则就是表达了这一层含义。
 *
 * @Author 网易云课堂微专业-java高级开发工程师【allen老师】
 * @Version 1.0
 */
class StudyA {
    public int func1(int a, int b) {
        return a - b;
    }

    public void newFun(Map map) {
        System.out.println("父类被执行了。。。。。");
    }
}

/*
后来，我们需要增加一个新的功能：完成两数相加，然后再与100求和，由类StudyB来负责。即类StudyB需要完成两个功能：
两数相加。
两数相加，然后再加100。
 */
class StudyB extends StudyA {
    @Override
    public int func1(int a, int b){
        return a + b;
    }

    public int func2(int a, int b){
        return func1(a,b) + 100;
    }

    // 将子类的方法参数范围缩小会怎么样？
    public void newFun(HashMap map) {
        System.out.println("子类被执行了。。。。。");
    }
}


public class LiskovSubstitutionPrinciple {

    public static void main(String[] args) {
        System.out.println("父类的运行结果：");
        StudyA a = new StudyA();
        System.out.println("100-50="+a.func1(100, 50));
        System.out.println("100-80="+a.func1(100, 80));
        /*
        运行结果：
        100-50=50
        100-80=20
         */

        // 父类存在的地方，可以用子类代替
        // 子类B替代父类
        System.out.println("子类的运行结果：");
        StudyB b = new StudyB();
        System.out.println("100+50=" + b.func1(100, 50));
        System.out.println("100+80=" + b.func1(100, 80));
        System.out.println("100 + 20 + 100=" + b.func2(100, 20));

        HashMap map = new HashMap();
        // 父类的新方法
        a.newFun(map);
//
//        // 父类存在的地方，可以用子类替代
//        // 子类B替代父类
        System.out.println("子类替代父类的运行结果：");
        b.newFun(map);
        /*
        运行结果：
        100-50=150
        100-80=180
        100+20+100=220
         */
    }

    /*
    我们发现原本运行正常的相减功能发生了错误。
    原因就是类StudyB在给方法起名时无意中重写了父类的方法，造成所有运行相减功能的代码全部调用了类StudyB重写后的方法，造成原本运行正常的功能出现了错误。
    在本例中，引用基类StudyA完成的功能，换成子类StudyB之后，发生了异常。在实际编程中，我们常常会通过重写父类的方法来完成新的功能，这样写起来虽然简单，
    但是整个继承体系的可复用性会比较差，特别是运用多态比较频繁时，程序运行出错的几率非常大。
    如果非要重写父类的方法，比较通用的做法是：原来的父类和子类都继承一个更通俗的基类，原有的继承关系去掉，采用依赖、聚合，组合等关系代替。
    */

}

